﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Threading;
using System.Collections;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls;

namespace MigratorUI.RenderEngine
{
    public class Scene : Canvas
    {
        public Scene() {
            this.Sprites = new Dictionary<string, Sprite>();
            this.AllowDrop = true;
            this.DragEnter += new System.Windows.DragEventHandler(SceneDragEnter);
            this.Drop += new System.Windows.DragEventHandler(SceneDragDrop);
            this.animationsQueue = new Queue<Animation>();
        }

        public void AttachAnimation(Animation animation) {
            this.animationsQueue.Enqueue(animation);
            this.ConsumeAnimation();
        }
        
        public void AttachSprite(Sprite sprite, Point position)
        {
            bool isNewSprite = false;
            //if is a new Sprite 
            if (!this.Sprites.ContainsValue(sprite))
            {
                this.Sprites.Add(sprite.Id, sprite);
                sprite.IdChangedEvent += new Sprite.IdChangedEventHandler(SpriteIdChangedHandler);
                sprite.SpriteRemovedEvent += new Sprite.SpriteRemovedEventHandler(SpriteRemovedHandler);
                sprite.SpriteMovedEvent += new Sprite.SpriteMovedEventHandler(SpriteMovedHandler);
                sprite.SpritedSelectedEvent += new Sprite.SpritedSelectedEventHandler(SpriteSelectedCompletedHandler);
                sprite.ViewPacketsReceivedEvent += new Sprite.ViewPacketsEventHandler(ViewPacketsReceivedHandler);
                sprite.ViewPacketsSentEvent += new Sprite.ViewPacketsEventHandler(ViewPacketsSentHandler);
                sprite.SendPacketsEvent += new Sprite.SendPacketsEventHandler(SendPacketsHandler);
                isNewSprite = true;
            }

            //if a new Sprite or Dragged Sprite
            if (!this.Children.Contains(sprite.GetDrawableItem()))
            {
                this.Children.Add(sprite.GetDrawableItem());
                position.X = position.X - sprite.LastClickedPointInDrawableItem.X;
                position.Y = position.Y - sprite.LastClickedPointInDrawableItem.Y;
                Canvas.SetLeft(sprite.GetDrawableItem(), position.X);
                Canvas.SetTop(sprite.GetDrawableItem(), position.Y);
                sprite.Position = position;
                ActivateSpriteAnimationCollection(sprite);
            }

            //fires attached event
            if (isNewSprite && SpriteAttachedEvent != null) SpriteAttachedEvent.Invoke(sprite);
            //fires moved event
            if (!isNewSprite && SpriteMovedEvent != null) SpriteMovedEvent.Invoke(sprite, new Point((sprite.Position.X - sprite.Image.Width / 2), (float)(sprite.Position.Y - sprite.Image.Height / 2)));
        }

        public void MoveSpriteTo(Sprite sprite, Point toPosition) {
            if (sprite.Position.X == toPosition.X && sprite.Position.Y == toPosition.Y)
                return;
            RemoveSpriteAnimationCollection(sprite);
            MovementAnimation moveAnimation = new MovementAnimation(sprite,false,toPosition);
            moveAnimation.AnimationCompletedEvent += new Animation.AnimationCompletedHandler(MoveCompletedHandler);
            moveAnimation.RunAnimation();
            sprite.Position = toPosition;
        }

        public void UpdateSprite(Sprite sprite)
        {
          RemoveSpriteAnimationCollection(sprite);
          ActivateSpriteAnimationCollection(sprite);     
        }

        public void RemoveSprite(Sprite sprite)
        {
            RemoveSpriteAnimationCollection(sprite);
            this.Sprites.Remove(sprite.Id);
            this.Children.Remove(sprite.GetDrawableItem());
        }

        private void ConsumeAnimation()
        {
            Animation animation = (Animation)this.animationsQueue.Dequeue();

         
            Sprite sprite = animation.Sprite;
            if (sprite != null)
            {
                //remove sprite temporary
                this.Children.Remove(sprite.GetDrawableItem());
                
                //if is repeat animation then all repeat animations can continues to run 
                if (animation.isRepeatAnimation) //is repeat animation that save it into list to repeat
                {
                    sprite.RepeatAnimationsList.Add(animation);
                }
                else
                { // if not repeat animation that we have stop all animation an run this 
                    foreach (Animation anim in sprite.RepeatAnimationsList)
                    {
                        this.Children.Remove(anim);
                    }
                    animation.AnimationCompletedEvent += new Animation.AnimationCompletedHandler(AnimationCompletedHandler);
                }

                if (!this.Children.Contains(animation))
                    this.Children.Add(animation);

                animation.RunAnimation();
                //redraw Sprite
                this.Children.Add(sprite.GetDrawableItem());
            }
        }

         private void AnimationCompletedHandler(Animation animation)
         {
            
            Sprite sprite = animation.Sprite;
            //remove animation 
            this.Children.Remove(animation);
            //remove sprite temporarily
            this.Children.Remove(sprite.GetDrawableItem());
            
            //redraw all repeat animations
            foreach (Animation anim in sprite.RepeatAnimationsList)
            {
                if (!this.Children.Contains(anim))
                    this.Children.Add(anim);
            }

            this.Children.Add(sprite.GetDrawableItem());
        }

         private void MoveCompletedHandler(Animation animation)
         {
             ActivateSpriteAnimationCollection(animation.Sprite);
         }
         
        private void SpriteSelectedCompletedHandler(Sprite sprite)
         {
             if (this.SpriteSelectedEvent != null) this.SpriteSelectedEvent.Invoke(sprite);
         }

        private void SceneDragEnter(object sender, DragEventArgs e)
        {
          
             if (e.Data.GetDataPresent(Sprite.spriteFormat.Name))
                 e.Effects = DragDropEffects.Copy;
             else
                 e.Effects = DragDropEffects.None;
        }

        //When object dragged is dropped
        private void SceneDragDrop(object sender, DragEventArgs e)
        {
            Sprite item = (Sprite)e.Data.GetData(Sprite.spriteFormat.Name);
            Point currentPosition = e.GetPosition(this);
            this.Children.Remove(item.GetDrawableItem());
            AttachSprite(item, currentPosition);   
        }

        /// <summary>
        /// Removes all animations associated an sprite 
        /// </summary>
        /// <param name="sprite"></param>
        private void RemoveSpriteAnimationCollection(Sprite sprite)
        {

            foreach (Animation animation in sprite.RepeatAnimationsList)
            {
                this.Children.Remove(animation);
            }
        }

        /// <summary>
        /// Activate all animations associated an sprite 
        /// </summary>
        /// <param name="sprite"></param>
        private void ActivateSpriteAnimationCollection(Sprite sprite)
        {
            this.Children.Remove(sprite.GetDrawableItem());
            foreach (Animation animation in sprite.RepeatAnimationsList)
            {
                if (!this.Children.Contains(animation))
                {
                    animation.UpdateAnimation(sprite);
                    this.Children.Add(animation);
                }
                animation.RunAnimation();
            }
            this.Children.Add(sprite.GetDrawableItem());
            Canvas.SetZIndex(sprite.GetDrawableItem(),100);
            Canvas.SetLeft(sprite.GetDrawableItem(),sprite.Position.X);
            Canvas.SetTop(sprite.GetDrawableItem(), sprite.Position.Y);
        }

        private void SpriteMovedHandler(Sprite sprite) {
            RemoveSpriteAnimationCollection(sprite);
        }

        private void SpriteIdChangedHandler(Sprite sprite, String oldId)
        {
            if (this.Sprites.ContainsKey(oldId))
                this.Sprites.Remove(oldId);
            if (!this.Sprites.ContainsKey(sprite.Id))
                this.Sprites.Add(sprite.Id, sprite);
         }

        private void SpriteRemovedHandler(Sprite sprite)
        {
            //removes all animations
            RemoveSpriteAnimationCollection(sprite);
            
            //Remove from List of sprites
            if (this.Sprites.ContainsKey(sprite.Id))
                this.Sprites.Remove(sprite.Id);
                
            //Remove from Canvas
            if (this.Children.Contains(sprite.GetDrawableItem()))
                this.Children.Remove(sprite.GetDrawableItem());
            
            //Fires event
            if (SpriteRemovedEvent != null) SpriteRemovedEvent.Invoke(sprite);
        }

        private void ViewPacketsReceivedHandler(Sprite sprite)
        {
            if (ViewPacketsReceivedEvent != null) ViewPacketsReceivedEvent.Invoke(sprite);
        }

        private void ViewPacketsSentHandler(Sprite sprite)
        {
            if (ViewPacketsSentEvent != null) ViewPacketsSentEvent.Invoke(sprite);
        }

        private void SendPacketsHandler(Sprite sprite)
        {
            if (SendPacketsEvent != null) SendPacketsEvent.Invoke(sprite);
        }

        private Queue<Animation> animationsQueue;
        public Dictionary<String, Sprite> Sprites { get; set; }


        public delegate void SpriteAttachedEventHandler(Sprite sprite);
        public event SpriteAttachedEventHandler SpriteAttachedEvent;
        
        public delegate void SpriteRemovedEventHandler(Sprite sprite);
        public event SpriteRemovedEventHandler SpriteRemovedEvent;

        public delegate void SpriteMovedEventHandler(Sprite sprite, Point newPosition);
        public event SpriteMovedEventHandler SpriteMovedEvent;

        public delegate void SpriteSelectedEventHandler(Sprite sprite);
        public event SpriteSelectedEventHandler SpriteSelectedEvent;

        public delegate void ViewPacketsEventHandler(Sprite sprite);
        public event ViewPacketsEventHandler ViewPacketsReceivedEvent;

        public event ViewPacketsEventHandler ViewPacketsSentEvent;

        public delegate void SendPacketsEventHandler(Sprite sprite);
        public event SendPacketsEventHandler SendPacketsEvent;

    }
}
